#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <unistd.h>

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "shm_array.h"

int const shm_type_sizes [n_shm_array_types] = {
	sizeof(char),
	sizeof(int32_t),
	sizeof(int64_t),
	sizeof(float),
	sizeof(double),
	sizeof(shm_cplxf_t),
	sizeof(shm_cplxd_t)
};

char const *shm_type_strings [n_shm_array_types] = {
	"char",
	"int32_t",
	"int64_t",
	"float",
	"double",
	"complex float",
	"complex double"
};


int
make_shm_array (char const *key, shm_array_t const *size, shm_array_t **mat)
{
	int64_t n;
	int i, shmid, err;

	*mat = 0;

	n = shm_type_sizes [size->type];

	for (i = 0; i < size->ndims; ++i)
		n *= size->dims[i];

	n += sizeof(shm_array_t);

	if (0 > (shmid = shm_open (key, O_RDWR | O_CREAT | O_TRUNC, 0666))){
		perror("make_shm_array:shm_open");
		return shmid;
	}

	if ((err = ftruncate (shmid, (off_t) n))){
		perror("make_shm_array:ftruncate");
		return err;
	}

	*mat = (shm_array_t*) mmap (0, n, PROT_READ | PROT_WRITE,
	                                    MAP_SHARED, shmid, 0);

	close (shmid);

	if (MAP_FAILED == *mat){
		perror("make_shm_array:mmap");
		*mat = 0;
		return -1;
	}


	(*mat)->ndims = size->ndims;

	for (i = 0; i < size->ndims; ++i)
		(*mat)->dims[i] = size->dims[i];

	(*mat)->region_size = n;
	(*mat)->type = size->type;

	return 0;
}

int
get_shm_array (char const *key, shm_array_t **mat)
{
	int shmid, n;

	*mat = 0;

	if (0 > (shmid = shm_open (key, O_RDWR, 0666))){
		perror("get_shm_array:shm_open");
		return shmid;
	}

	if (-1 == (n = lseek (shmid, 0, SEEK_END))){
		perror("get_shm_array:lseek");
		return -1;
	}
	lseek (shmid, 0, SEEK_SET);

	*mat = (shm_array_t*) mmap (0, n, PROT_READ | PROT_WRITE,
	                                      MAP_SHARED, shmid, 0);

	close (shmid);

	if (MAP_FAILED == *mat){
		perror("get_shm_array:mmap");
		return -1;
	}

	return 0;
}

int
put_shm_array (char const *key, shm_array_t *mat)
{
	return munmap ((void*) mat, mat->region_size);
}

int
del_shm_array (char const *key)
{
	int err;
	if ((err = shm_unlink (key))){
		perror("del_shm_array:shm_unlink");
		return err;
	}

	return 0;
}

